<?php
include('Text/Highlighter.php');
include('Text/Highlighter/Renderer/Html.php');
require_once('../inc/enum.php');
require_once('../inc/utils.php');

function main()
{
	global $g_tplvars;
	
	if(!isset($_GET['action']))
	{
		header('Location: projects.php?action=summary');
		exit;
	}
	
	$action = $_GET['action'];

	//legal actions
	$actions = array(
		'summary',
		'create',
		'docreate',
		'detail',
		'addrepo',
		'doaddrepo',
		'changes',
		'showfile',
		'diff',
		'addissue',
		'doaddissue',
		'issues',
		'showissue',
		'edit'
		);
		
	if(!in_array($_GET['action'], $actions))
		summary();
	else
		$_GET['action']();
}

/**
	@brief Determines what we're allowed to see
	
	@param a Project row
	@param access Access requested ('read', 'write', 'admin')
 */
function can_access($a, $access)
{
	$uid = $_SESSION['uid'];
	$lvl = enum_value_to_name('publevels', $a->publicity);

	$k = dbquery('select * from `projmembers` where `project` = \'' . $a->id . '\' and `uid` = \'' . $uid . '\'');
	$acc = 'none';
	if(mysql_num_rows($k) != 0)
		$acc = enum_value_to_name('memberlevels', mysql_fetch_object($k)->access);
	
	//Public: Anyone can see, members can write, owner can admin
	if($lvl == 'public')
	{
		switch($access)
		{
		case 'read':
			return true;
		case 'write':
			return $acc != 'none';
		case 'admin':
			return $acc == 'developer';
		default:
			return false;
		}
	}
	
	//Protected: All registered users can see, members can write, owners can admin
	if($lvl == 'protected')
	{
		switch($access)
		{
		case 'read':
			return $uid > 0;
		case 'write':
			return $acc != 'none';
		case 'admin':
			return $acc == 'developer';
		default:
			return false;
		}
	}
	
	//Private: Members can read and write, owners can admin
	if($lvl == 'private')
	{
		switch($access)
		{
		case 'read':
		case 'write':
			return $acc != 'none';
		case 'admin':
			return $acc == 'developer';
		default:
			return false;
		}
	}
	
	//Something else? Default deny
	return false;
}

function permcheck($id, $access)
{
	global $g_tplvars;
	
	//Permissions check
	$r = dbquery('select * from `projects` where `id` = \'' . $id . '\' limit 1');
	if(mysql_num_rows($r) != 1)
	{
		//TODO: error
		$g_tplvars['title'] = 'Invalid project';
		render('../templates/projects-detail.html');
		exit;
	}
	$project = mysql_fetch_object($r);
	if(!can_access($project, $access))
	{
		$g_tplvars['title'] = 'Access denied';
		render('../templates/access-denied.html');
		exit;
	}
}

/**
	@brief Displays list of projects
 */
function summary()
{
	global $g_tplvars;
	
	//Update projects
	$r = dbquery('select `id` from `projects`');
	while($a = mysql_fetch_object($r))
		vcs_update($a->id);
	
	//Build list of active projects
	$projects = '';
	$r = dbquery('select * from `projects` order by `desc`');
	while($a = mysql_fetch_object($r))
	{
		if(!can_access($a, 'read'))
			continue;		
		
		foreach($a as $n=>$v)
			$g_tplvars[$n] = $v;			
		$g_tplvars['time'] = format_date($a->mtime);
		$g_tplvars['class'] = 'projectrow';
		$g_tplvars['access'] = enum_value_to_name('publevels', $a->publicity);
		$g_tplvars['status'] = enum_value_to_name('projstatus', $a->status);
		if(can_access($a, 'write'))
			$g_tplvars['actions'] = templatize('../templates/projects-summary-item-actions.html');
		else
			$g_tplvars['actions'] = '';
		$projects .= templatize('../templates/projects-summary-item.html');
	}
	
	$g_tplvars['title'] = 'Project List';
	$g_tplvars['projects'] = $projects;	
	render('../templates/projects-summary.html');
}

/**
	@brief Create a new project
 */
function create()
{
	//all members can create
	restricted();
	
	global $g_tplvars;
	$g_tplvars['title'] = 'Create Project';
	render('../templates/projects-create.html');
}

/**
	@brief Handles creation of a new project
 */
function docreate()
{
	//all members can create
	restricted();
	
	//Prepare input
	$desc = sanitize_db(sanitize_render($_POST['desc']));
	$longdesc = sanitize_db(sanitize_render($_POST['longdesc']));
	$uid = $_SESSION['uid'];
	$mtime = time();
	
	$devacc = enum_name_to_value('memberlevels','developer');
	$publicity = enum_name_to_value('publevels', 'private');
		
	//Add to the database
	dbquery(
			'insert into projects(`desc`,`longdesc`, `mtime`, `publicity`) '.
			'values(\'' . $desc . '\', \'' . $longdesc . '\', \'' . $mtime . '\', \'' . $publicity . '\')'
		);
	$pid = mysql_insert_id();
		
	dbquery(
			'insert into projmembers(`project`,`uid`, `access`) '.
			'values(\'' . $pid . '\', \'' . $uid . '\', \'' . $devacc . '\')'
		);
	
	//and go back to the overview
	header('Location: projects.php?action=summary');
}

/** 
	@brief Displays detailed info about a project
 */
function detail()
{
	global $g_tplvars;
	
	$id = intval($_GET['id']);
	permcheck($id, 'read');
	
	if(!isset($_GET['newest']))
		$newest = time();
	else
		$newest = intval($_GET['newest']);
	$count = 10;
	
	//Pull any repo changes from disk before going further
	//so this render will reflect the new version
	vcs_update($id);
	
	//Look up the project
	$r = dbquery('select * from `projects` where `id` = \'' . $id . '\' limit 1');
	if(mysql_num_rows($r) != 1)
	{
		//TODO: error
		$g_tplvars['title'] = 'Invalid project';
		render('../templates/projects-detail.html');
		exit;
	}
	
	$project = mysql_fetch_object($r);
	
	$g_tplvars['title'] = $project->desc;
	$g_tplvars['longdesc'] = $project->longdesc;
	$g_tplvars['time'] = format_date($project->mtime);
		
	//Get members
	$members = '';
	$r = dbquery('select * from `projmembers` where `project` = \'' . $id . '\' order by access asc');
	while($line = mysql_fetch_object($r))
	{
		$g_tplvars['uid'] = $line->uid;
		$g_tplvars['commits'] = vcs_commit_count($id, $line->uid);
		$g_tplvars['name'] = uid_to_name($line->uid);
		$g_tplvars['position'] = enum_value_to_name('memberlevels', $line->access);
		$members .= templatize('../templates/projects-member.html');
	}
	$g_tplvars['members'] = $members;
	
	$r = dbquery('select `id` from `issues` where `project` = \'' . $id . '\' and `status` >= \'0\'');
	$g_tplvars['openbugs'] = mysql_num_rows($r);
	$r = dbquery('select `id` from `issues` where `project` = \'' . $id . '\'');
	$g_tplvars['totalbugs'] = mysql_num_rows($r);
	
	//Look up repos
	$g_tplvars['repos'] = vcs_browser($id);
	$g_tplvars['history'] = vcs_history($id, $newest, $count);
	
	render('../templates/projects-detail.html');
}

/**
	@brief Prompts to add a new repository to a project
 */
function addrepo()
{
	global $g_tplvars;
	
	$id = intval($_GET['id']);
	permcheck($id, 'admin');
	if($id <= 0)
	{
		//todo: error
		header('Location: projects.php?action=summary');
		exit;
	}
	
	$g_tplvars['id'] = $id;
	$g_tplvars['title'] = 'Add Repository';
	render('../templates/projects-addrepo.html');
}

/**
	@brief Handles addition of a new repo
 */
function doaddrepo()
{
	$id = intval($_GET['id']);
	permcheck($id, 'admin');
	
	//Prepare input
	$url = sanitize_db(sanitize_render($_POST['url']));
	$vcs = intval($_POST['vcs']);
	
	//only SVN supported for now
	if($vcs != VCS_SVN)
	{
		//todo: error
		header('Location: projects.php?action=summary');
		exit;
	}
		
	//Add to the database
	dbquery(
			'insert into repos(`project`,`url`,`vcs`,`cachedrev`) '.
			'values(\'' . $id . '\', \'' . $url . '\', \'' . $vcs . '\', \'0\')'
		);
	
	header('Location: projects.php?action=detail&id=' . $id);
}

/**
	@brief Displays changes made in a specific version
 */
function changes()
{	
	global $g_tplvars;
	
	$id = intval($_GET['id']);
	$repo = intval($_GET['repo']);
	$rev = intval($_GET['rev']);
	$g_tplvars['id'] = $id;
	
	permcheck($id, 'read');
	
	//Pull any repo changes from disk before going further
	//so this render will reflect the new version
	vcs_update($id);
	
	//Look up the project
	$r = dbquery('select * from `projects` where `id` = \'' . $id . '\' limit 1');
	if(mysql_num_rows($r) != 1)
	{
		//TODO: error
		$g_tplvars['title'] = 'Invalid project';
		render('../templates/projects-detail.html');
		exit;
	}
	$proj = mysql_fetch_object($r);
	
	//Get title
	$g_tplvars['title'] = $proj->desc . ' revision ' . $rev;
	
	//Dump the log
	$g_tplvars['changes'] = vcs_changes($repo, $rev);	
	render('../templates/projects-vcs-changes.html');
}

/**
	@brief Displays a file at a specific version
 */
function showfile()
{
	global $g_tplvars;
	
	$id = intval($_GET['id']);
	permcheck($id, 'read');
	
	//Pull any repo changes from disk before going further
	//so this render will reflect the new version
	vcs_update($id);
	
	//TODO: project name in title?
	//TODO: handle binary files / images
	//TODO: check for directory traversal in filename if this is possible in svn
	
	$repo = intval($_GET['repo']);
	$rev = intval($_GET['rev']);
	$file = sanitize_render($_GET['file']);
	
	$g_tplvars['file'] = $file;
	$g_tplvars['id'] = $id;
	$g_tplvars['rev'] = $rev;
	$g_tplvars['title'] = $file . ' revision ' . $rev;
	
	$str = vcs_file_contents($repo, $rev, $file);
	
	//Set up syntax highlighting
	$highlight = true;
	if(substr_count($file, '.php'))
		$hl = Text_Highlighter::factory('PHP');
	else if(substr_count($file, '.css'))			//must come before .c
		$hl = Text_Highlighter::factory('CSS');
	else if(substr_count($file, '.c') || substr_count($file, '.h'))
		$hl = Text_Highlighter::factory('CPP');
	else if(substr_count($file, '.htm'))
		$hl = Text_Highlighter::factory('HTML');
	else
		$highlight = false;
		
	if($highlight)
	{
		//Highlight the string
		$rend = new Text_Highlighter_Renderer_Html(array('numbers' => HL_NUMBERS_LI, 'tabsize' => 4));
		$hl->setRenderer($rend);
		$high = $hl->highlight($str);
		
		//force proper indentation
		//TODO: find more efficient way of doing this
		for($i = 0; $i < 10; $i ++)
		{
			$high = str_replace('&nbsp;   ', '&nbsp;&nbsp;&nbsp;&nbsp;', $high);
			$high = str_replace('  ', '&nbsp;&nbsp;', $high);
			$high = str_replace('&nbsp; ', '&nbsp;&nbsp;', $high);
			if(substr_count($high, '&nbsp; ') == 0)
				break;
		}
		
		//and render
		$g_tplvars['text'] = $high;
		render('../templates/projects-vcs-file.html');
	}
	else
	{
		$g_tplvars['text'] = htmlentities($str);
		render('../templates/projects-vcs-file-generic.html');
	}
}

/**
	@brief Shows changes since the last revision of a file
 */
function diff()
{
	global $g_tplvars;
	
	$id = intval($_GET['id']);
	$repo = intval($_GET['repo']);
	$rev = intval($_GET['rev']);
	$oldrev = $rev - 1;
	$file = sanitize_render($_GET['file']);
	permcheck($id, 'read');

	$g_tplvars['file'] = $file;
	$g_tplvars['id'] = $id;
	$g_tplvars['rev'] = $rev;
	$g_tplvars['title'] = 'Diff of ' . $file . ' revisions ' . $rev . ' and ' . $oldrev;
	
	$diff = vcs_file_diff($repo, $rev, $oldrev, $file);
		
	$g_tplvars['diff'] = $diff;
	
	render('../templates/projects-vcs-diff.html');
}

/**
	@brief Prompts to add a new issue
 */
function addissue()
{
	global $g_tplvars;
	$id = intval($_GET['id']);
	permcheck($id, 'write');
	
	$g_tplvars['id'] = $id;
	$g_tplvars['title'] = 'New Issue';
	
	$r = dbquery('select * from users order by `name` asc');
	$str = '';
	while($line = mysql_fetch_object($r))
	{
		$g_tplvars['uid'] = $line->uid;
		$g_tplvars['name'] = $line->name;
		if($line->uid < 0)
			$g_tplvars['selected'] = 'selected=\'1\'';
		else
			$g_tplvars['selected'] = '';
		$str .= templatize('../templates/username-selector-line.html');
	}
	$g_tplvars['userlist'] = $str;
	$g_tplvars['prilist'] = enum_list('priorities', 0);
	$g_tplvars['stalist'] = enum_list('bugstatus', 0);
	$g_tplvars['typelist'] = enum_list('issuetype', 0);
	
	render('../templates/projects-new-issue.html');
}

/**
	@brief Adds an issue to a project
 */
function doaddissue()
{
	$id = intval($_GET['id']);
	permcheck($id, 'write');
	
	$title = sanitize_db(sanitize_render($_POST['title']));
	$assignedto = intval($_POST['assignedto']);
	$priority = intval($_POST['priority']);
	$type = intval($_POST['type']);
	$status = intval($_POST['status']);
	$desc = sanitize_db(sanitize_render($_POST['desc']));
	$time = time();
	
	dbquery('insert into issues (`title`,`project`,`assignedto`,`itype`,`status`,`modtime`,`body`) ' .
			"VALUES( '$title', '$id', '$assignedto', '$type', '$status', '$time', '$desc')");
	
	header('Location: projects.php?action=issues&id=' . $id);
}

/**
	@brief Displays issues related to a project
 */
function issues()
{
	global $g_tplvars;
	
	$id = intval($_GET['id']);
	$g_tplvars['id'] = $id;
	permcheck($id, 'read');
	
	//Look up the project
	$r = dbquery('select * from `projects` where `id` = \'' . $id . '\' limit 1');
	if(mysql_num_rows($r) != 1)
	{
		//TODO: error
		$g_tplvars['title'] = 'Invalid project';
		render('../templates/projects-detail.html');
		exit;
	}
	$project = mysql_fetch_object($r);
	$g_tplvars['title'] = 'Issues for ' . $project->desc;
	
	//See if we are showing open, closed, or all
	$minfilter = 0;
	$maxfilter = 999;
	if(isset($_GET['showclosed']) && (intval($_GET['showclosed']) == 1))
		$minfilter = -999;
		
	//Find the issues
	$r = dbquery('select * from `issues` where `project` = \'' . $id . '\' and `status` >= \'' . $minfilter . '\'' .
		' and `status` <= \'' . $maxfilter . '\' order by `modtime` desc');
	$issues = '';
	while($line = mysql_fetch_object($r))
	{
		$g_tplvars['bugid'] = $line->id;
		$g_tplvars['bugtitle'] = $line->title;
		$g_tplvars['status'] = enum_value_to_name('bugstatus', $line->status);
		$g_tplvars['assignedto'] = $line->assignedto;
		$g_tplvars['assignedname'] = uid_to_name($line->assignedto);
		$g_tplvars['modtime'] = format_date($line->modtime);
		$issues .= templatize('../templates/projects-issue-list-item.html');
	}
	
	$g_tplvars['issues'] = $issues;
	
	render('../templates/projects-issue-list.html');
}

/**
	@brief Shows details for a single issue
 */
function showissue()
{
	global $g_tplvars;
	$id = intval($_GET['id']);
	$issue = intval($_GET['issue']);
	$g_tplvars['id'] = $id;
	$g_tplvars['issue'] = $issue;
	permcheck($id, 'read');
	
	//Look up the project
	$r = dbquery('select * from `projects` where `id` = \'' . $id . '\' limit 1');
	if(mysql_num_rows($r) != 1)
	{
		//TODO: error
		$g_tplvars['title'] = 'Invalid project';
		render('../templates/issue-detail.html');
		exit;
	}
	$project = mysql_fetch_object($r);
	
	//Look up the issue
	$r = dbquery('select * from `issues` where `id` = \'' . $issue . '\' limit 1');
	if(mysql_num_rows($r) != 1)
	{
		//TODO: error
		$g_tplvars['title'] = 'Invalid issue ID';
		render('../templates/issue-detail.html');
		exit;
	}
	$iss = mysql_fetch_object($r);
	
	$g_tplvars['title'] = 'Issue ' . $issue . ': ' . $iss->title;
	
	$g_tplvars['body'] = nl2br($iss->body);
	$g_tplvars['bugtitle'] = $iss->title;
	$g_tplvars['status'] = enum_value_to_name('bugstatus', $iss->status);
	$g_tplvars['assignedto'] = $iss->assignedto;
	$g_tplvars['assignedname'] = uid_to_name($iss->assignedto);
	$g_tplvars['modtime'] = format_date($iss->modtime);
	
	render('../templates/projects-issue-detail.html');
}

/**
	@brief Edit a project
 */
function edit()
{
	$id = intval($_GET['id']);
	permcheck($id, 'write');
	render('../templates/projects-edit.html');
}
?>
